#include <iostream>
#include <typeinfo>

using std::endl;
using std::cout;

enum Type {
    T_NO_TYPE_OR_NOTPUB, //0: 没有type成员或者非public
    T_INT,  //1: int
    T_PTR, //2: 普通指针
    T_CONST_PTR, //3: 常量指针
    T_FOO, //4: Foo
    T_BAR, //5: Bar
    T_NO_CONST, //6: 成员type无const修饰: static Type C::type;
    T_NO_STATIC, //7: 成员type非静态：const Type C::type;
    T_NE_CONST_NOR_STATIC, //8: 成员type无const无static: Type C::type;
    T_NOTTYPE, //9: 有type成员但非Type类型 
    T_TYPE, //10: 成员type是一种类型：typedef <type> C::type; 
    T_MEM_FUNC_PTR, //11: 成员函数指针: R(C::*)(ArgTypes...); 
    T_ERR, //12: 匹配异常
};

struct Foo {
    const static Type type = T_FOO;
};


struct Bar {
    const static Type type = T_BAR;
};

struct Bug {
    static Type type;
};

Type Bug::type = T_BAR;

struct Bug2 {
    const Type type;
};

struct Bug3 {
    Type type;
};

struct Bug4 {
    static const int type=1;
};

struct Bug5 {
private:
    static const Type type=T_ERR; //private can not access out of class
};

struct Bug6 {
    static const Type type() {return T_ERR;} 
};

struct Bug7 {
    typedef Type type;
};

template <typename T> //尝试获取const static Type T::type
struct enum_traits {
    template<typename C> //match: const Type C::type;
    static constexpr Type _get_type(const Type C::*tp) { return T_NO_STATIC; }

    template<typename C> //match: Type C::type; 
    static constexpr Type _get_type(Type C::*tp) { return T_NE_CONST_NOR_STATIC; }

    //match: static const Type [T::]type;
    static constexpr Type _get_type(const Type *tp) { return *tp; }

    //match: static Type [T::]type;
    static constexpr Type _get_type(Type *tp) { return T_NO_CONST; }

    //match: other conditions: not Type [or not public]
    static constexpr Type _get_type(...) { return T_NOTTYPE; }
    
    template<typename C> //match: public [static const] <type> C::type
    static constexpr Type get_type(decltype(&C::type)) { return _get_type(&C::type); }

    template<typename C> //match: typedef <type> C::type
    static constexpr Type get_type(typename C::type *) { return T_TYPE; }

    template<typename C> //match: other conditions: no type or not public
    static constexpr Type get_type(...) {return T_NO_TYPE_OR_NOTPUB; }

    
    static const Type type = get_type<T>(0);
};


template<> //match int
struct enum_traits<int> {
    static const Type type = T_INT;
};

template<typename T> //match 普通指针
struct enum_traits<T*> {
    static const Type type = T_PTR;
};

template<typename T>//匹配常量指针
struct enum_traits<const T*> {
    static const Type type = T_CONST_PTR;   
};

//匹配成员函数指针
template<typename C, typename R, typename ...ArgTypes>
struct enum_traits<R(C::*)(ArgTypes...)> {
    static const Type type = T_MEM_FUNC_PTR;   
};

int main(){
    cout << enum_traits<int>::type << endl;
    cout << enum_traits<int*>::type << endl;
    cout << enum_traits<const int*>::type << endl;
    cout << enum_traits<Foo>::type << endl;
    cout << enum_traits<Bar>::type << endl;
    cout << enum_traits<double>::type << endl;
    cout << enum_traits<Bug>::type << endl;
    cout << enum_traits<Bug2>::type << endl;
    cout << enum_traits<Bug3>::type << endl;
    cout << enum_traits<Bug4>::type << endl;
    cout << enum_traits<Bug5>::type << endl;
    cout << enum_traits<Bug6>::type << endl;
    cout << enum_traits<Bug7>::type << endl;
    cout << enum_traits<const Type (Bug6::*)()>::type << endl;
    return 0;
}
